package com.apobates.jforum.grief.aurl.utils.defend;

import com.apobates.jforum.grief.aurl.utils.Pair;
import com.apobates.jforum.grief.aurl.utils.Tuple;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.function.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 防卫检查器
 * @param <T> 判断的目标类型
 */
public class DefendAssert<T> {
    private final Assertion<T> assertion;
    private final PredicateQueue<T> predicateQueue;
    private final static Logger logger = LoggerFactory.getLogger(DefendAssert.class);

    /**
     * 初始化判断的目标
     * @param assertion 断言校验
     */
    public DefendAssert(Assertion<T> assertion, PredicateQueue<T> predicateQueue) {
        this.assertion = assertion;
        this.predicateQueue = predicateQueue;
    }


    /**
     * 空参数的检查
     * @return
     * @param <T>
     */
    public static <T> DefendAssert<T> empty(){
        return new DefendAssert<>(Assertion.empty(), PredicateQueue.empty());
    }

    /**
     * 若参数校验失败直接抛出异常,反之继续当前方法
     * @param otherIns
     * @return
     * @param <R>
     */
    public <R> DefendAssert<T> before(DefendAssert<R> otherIns){
        if(null==otherIns || otherIns.isEmpty()) {
            return this;
        }
        otherIns.suspend();
        return this;
    }

    /**
     * 若当前校验失败直接抛出异常,反之交由参数
     * @param otherIns
     * @return
     * @param <R>
     */
    public <R> DefendAssert<R> after(DefendAssert<R> otherIns){
        if(!isEmpty()){
            suspend();
        }
        return null==otherIns?DefendAssert.empty():otherIns;
    }

    /**
     * 执行校验
     * 注意: 在第一个规则校验失败即中止校验
     * 适用于: single和multi创建的实例
     * @param conditions 附加的校验条件链
     * @param handler 校验结果的处理句柄
     * @return
     * @param <R>
     */
    public <R> R execute(PredicateQueue<T> conditions, AssertResultHandler<T,R> handler){
        Objects.requireNonNull(handler);
        this.predicateQueue.contact(conditions);
        List<AssertResult<T>> assertResults = this.assertion.asserte(predicateQueue);
        return handler.handle(assertResults);
    }

    /**
     * 执行校验。
     * 注意: 在第一个规则校验失败即中止校验
     * 适用于: single和multi创建的实例
     * @param handler 校验结果的处理句柄
     * @return
     * @param <R>
     */
    public <R> R execute(AssertResultHandler<T,R> handler){
        Objects.requireNonNull(handler);
        List<AssertResult<T>> assertResults = this.assertion.asserte(this.predicateQueue);
        return handler.handle(assertResults);
    }

    /**
     * 执行全校验, 并返回错误消息
     * 注意: 使用默认的消息格式化器. 只输出: 校验的label和message,格式:[%s]%s
     * @return
     */
    public Stream<Pair<Integer,String>> getError(){
        Function<Map<Integer,List<String>>, Stream<Pair<Integer,String>>> fun = (map)->{
            return map.entrySet().stream().map(entry -> Pair.of(entry.getKey(), String.join(", ", entry.getValue())));
        };

        return this.validate(fun, (ar)->String.format("[%s]%s", ar.label(), ar.message()));
    }

    /**
     * 执行全校验, 出错时不抛异常, 不对校验目标进行处理
     * 适用于: single和multi创建的实例
     * @param handler 校验结果的处理句柄. 参数: key=目标下标. single时是0,multi时是目标对应的下载; value=失败消息列表
     * @param messageFormat 失败消息格式化器
     */
    public <R> R validate(Function<Map<Integer,List<String>>, R> handler, Function<AssertResult<T>, String> messageFormat){
        Objects.requireNonNull(handler);
        AssertResultValidateHandler<T> arvh = new AssertResultValidateHandler<>(messageFormat);
        List<AssertResult<T>> results = Collections.emptyList();
        if(this.assertion instanceof SingleAssertion<T> singleAssertion){
            results = singleAssertion.toCompleteInstance().asserte(this.predicateQueue);
        }
        if(this.assertion instanceof MultiAssertion<T> multiAssertion){
            results = multiAssertion.toCompleteInstance().asserte(this.predicateQueue);
        }
        Map<Integer, List<String>> errors = arvh.handle(results);
        return handler.apply(errors);
    }

    /**
     * 执行中断校验, 失败时抛出:IllegalArgumentException
     * 适用于: single和multi创建的实例
     * @param conditions 附加的校验条件链
     */
    public void suspend(PredicateQueue<T> conditions){
        this.execute(conditions, AssertResultSuspendHandler.of(IllegalArgumentException.class));
    }

    /**
     * 执行中断校验, 失败时抛出:IllegalArgumentException
     * 适用于: single和multi创建的实例
     */
    public void suspend(){
        this.execute(AssertResultSuspendHandler.of(IllegalArgumentException.class));
    }

    /**
     * 执行中断校验, 失败时抛出指定的异常
     * 适用于: single和multi创建的实例
     * @param conditions 附加的校验条件链
     * @param exception 指定的异常. 若不可用抛出:IllegalArgumentException
     */
    public void suspend(PredicateQueue<T> conditions, Supplier<? extends RuntimeException> exception){
        Objects.requireNonNull(exception);
        this.execute(conditions, AssertResultSuspendHandler.of(exception));
    }

    /**
     * 执行中断校验, 失败时抛出指定的异常
     * 适用于: single和multi创建的实例
     * @param exception 指定的异常. 若不可用抛出:IllegalArgumentException
     */
    public void suspend(Supplier<? extends RuntimeException> exception){
        Objects.requireNonNull(exception);
        this.execute(AssertResultSuspendHandler.of(exception));
    }

    /**
     * 执行校验并转换校验目标
     * 注意: 在第一个规则校验失败即中止校验
     * @param function 包装映射函数. 第一个参数: 表示所有校验的目标;第二个参数:表示校验中失败的目标索引
     * @param <R>
     */
    public <R> R get(BiFunction<List<T>, List<Integer>, R> function){
        if(null == function){
            throw new IllegalArgumentException("参数指向的函数不可用");
        }
        return this.execute(new AssertResultConvertHandler<>(function));
    }

    /**
     * 执行校验并用默认值替换掉校验失败的目标
     * 注意: 在第一个规则校验失败即中止校验
     * @param defaultValue 默认值. 不要对计算类的默认值使用, 只建议是常量默认值
     * @return
     */
    public List<T> getElse(final T defaultValue){
        return this.execute((List<AssertResult<T>> results)-> {
            return results.stream().map(ar->ar.result()?ar.target():defaultValue).collect(Collectors.toList());
        });
    }

    /**
     * 执行校验, 失败时返回Optional.empty, 成功时返回包装的参数值
     * 注意: 在第一个规则校验失败即中止校验
     * 适用于: single创建的实例
     * @return
     */
    public Optional<T> get(){
        return this.get((List<T> rs, List<Integer> failIndexList)->{
            if(failIndexList.isEmpty() && rs.size()==1){
                return Optional.ofNullable(rs.get(0));
            }
            return Optional.empty();
        });
    }

    /**
     * 执行校验, 失败时返回Optional包装的方法参数值, 成功时返回包装的实例化参数值
     * 注意: 在第一个规则校验失败即中止校验
     * 适用于: single创建的实例
     * @param defaultValue 默认值. 不要对计算类的默认值使用, 只建议是常量默认值
     * @return
     */
    public Optional<T> get(final T defaultValue){
        return this.get((List<T> rs, List<Integer> failIndexList)->{
            if(rs.size()==1){
                return (failIndexList.isEmpty())?Optional.ofNullable(rs.get(0)):Optional.ofNullable(defaultValue);
            }
            return Optional.empty();
        });
    }

    /**
     * 执行校验, 失败时返回Optional包装的方法参数值, 成功时返回包装的实例化参数值
     * 注意: 在第一个规则校验失败即中止校验
     * 适用于: single创建的实例
     * @param defaultValue 默认值. 适用于默认值非常量
     * @return
     */
    public Optional<T> get(final Supplier<T> defaultValue){
        return this.get((List<T> rs, List<Integer> failIndexList)->{
            if(rs.size()==1){
                return (failIndexList.isEmpty())?Optional.ofNullable(rs.get(0)):Optional.ofNullable(defaultValue.get());
            }
            return Optional.empty();
        });
    }

    /**
     * 当前是否空, 表示:不执行校验并总是通过
     * @return true是,false不是
     */
    public boolean isEmpty(){
        return this.assertion instanceof EmptyAssertion;
    }

    /**
     * 中断校验处理句柄
     * @param <T>
     */
    private static class AssertResultSuspendHandler<T> implements AssertResultHandler<T,Void>{
        private final Class<? extends RuntimeException> classIns;

        private AssertResultSuspendHandler(Class<? extends RuntimeException> classIns) {
            this.classIns = classIns;
        }

        public static <T> AssertResultSuspendHandler<T> of(Supplier<? extends RuntimeException> exception){
            return new AssertResultSuspendHandler<>(exception.get().getClass());
        }

        public static <T> AssertResultSuspendHandler<T> of(Class<? extends RuntimeException> classIns){
            return new AssertResultSuspendHandler<>(classIns);
        }

        @Override
        public Void handle(List<AssertResult<T>> results) {
            Tuple<Integer,Boolean,String> process = process(results);
            if(!process.middle()){
                throw buildException(process.right());
            }
            return null;
        }

        private Tuple<Integer,Boolean,String> process(List<AssertResult<T>> results){
            Optional<AssertResult<T>> first = results.stream().filter(ele->!ele.result()).findFirst();
            return first.isPresent()? new Tuple(first.get().offset(), false, first.get().message()):new Tuple<>(-1, true, "OK");
        }

        private RuntimeException buildException(String message){
            try {
                Constructor<? extends RuntimeException> constructor = classIns.getDeclaredConstructor(String.class);
                RuntimeException runtimeException = constructor.newInstance(message);
                return runtimeException;
            } catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
                return new IllegalArgumentException(message);
            }

        }
    }

    /**
     * 转换校验处理句柄
     * @param <T>
     * @param <R>
     */
    private static class AssertResultConvertHandler<T,R> implements AssertResultHandler<T,R>{
        private BiFunction<List<T>, List<Integer>, R> function;

        public AssertResultConvertHandler(BiFunction<List<T>, List<Integer>, R> function) {
            this.function = function;
        }

        @Override
        public R handle(List<AssertResult<T>> results) {
            Pair<List<Integer>, List<T>> data = extract(results);
            return function.apply(data.right(), data.left());
        }

        /**
         * 解压出结果: 关心失败元素的下标和所有校验目标
         * @param result left= 失败元素的下标
         *               right= 所有的判断目标
         * @return
         */
        private Pair<List<Integer>, List<T>> extract(List<AssertResult<T>> result) {
            List<Integer> errIndex =new ArrayList<>();
            List<T> targetList = new ArrayList<>(result.size());
            for (AssertResult<T> ele: result) {
                targetList.add(ele.target());
                if(!ele.result()){
                    errIndex.add(ele.offset());
                }
            }
            return Pair.of(errIndex, targetList);
        }
    }

    private static class AssertResultValidateHandler<T> implements AssertResultHandler<T, Map<Integer,List<String>>>{
        private final Function<AssertResult<T>, String> messageFormat;

        private AssertResultValidateHandler(Function<AssertResult<T>, String> messageFormat) {
            this.messageFormat = messageFormat;
        }

        @Override
        public Map<Integer,List<String>> handle(List<AssertResult<T>> assertResults) {
            Map<Integer, List<String>> rs = new HashMap<>();
            for (AssertResult<T> ar: assertResults) {
                if(!ar.result()){ // 失败
                    String errorMessage = messageFormat.apply(ar); // String.format("[%s]%s", ar.label(), ar.message());
                    if(rs.containsKey(ar.offset())){
                        rs.get(ar.offset()).add(errorMessage);
                    }else{
                        rs.put(ar.offset(), new ArrayList<>(Arrays.asList(errorMessage)));
                    }
                }
            }
            return rs;
        }

    }
}
